package com.lyc.lojbackendjudgeservice.judge.strategy.impl;
import cn.hutool.json.JSONUtil;
import com.lyc.lojbackendjudgeservice.judge.strategy.JudgeContext;
import com.lyc.lojbackendjudgeservice.judge.strategy.JudgeStrategy;
import com.lyc.lojbackendmodel.model.codesandbox.JudgeInfo;
import com.lyc.lojbackendmodel.model.dto.question.JudgeCase;
import com.lyc.lojbackendmodel.model.dto.question.JudgeConfig;
import com.lyc.lojbackendmodel.model.entity.Question;
import com.lyc.lojbackendmodel.model.enums.JudgeInfoMessageEnum;

import java.util.List;
import java.util.Optional;

public abstract class JudgeStrategyTemplate implements JudgeStrategy {

    // 成功状态值
    private static final int STATUS_SUCCESS = 1;
    // 编译错误或者系统错误状态值
    private static final int STATUS_COMPILE_SYSTEM_ERROR = 2;
    // 运行错误状态值
    private static final int STATUS_RUNTIME_ERROR = 3;
    
    JudgeInfoMessageEnum judgeInfoMessageEnum;
    
    // 判题思路
    /*
    * ● 根据status的值赋值返回信息
       ○ 如果是3，返回运行错误
       ○ 如果是2，并且message中含有编译错误，返回编译错误
       ○ 如果是1，进一步判断
    ● 如果status为1，判断时间，内存是否超过题目限制，如果超过某一个返回超时或内存溢出
    ● 最后根据isManual的值分析
      ○ 如果是1，人工测评，返回输出信息
      ○ 如果是0，不是人工测评，返回初始赋值的信息（成功）
      **/

    /**
     * 根据代码沙箱的状态分析判题
     * @param judgeContext
     * @return
     */
    @Override
    public JudgeInfo doJudge(JudgeContext judgeContext) {
        int status = judgeContext.getStatus();
        switch (status) {
            case STATUS_COMPILE_SYSTEM_ERROR:
                return handleCompileOrSystemError(judgeContext);
            case STATUS_RUNTIME_ERROR:
                return handleRuntimeError(judgeContext);
            case STATUS_SUCCESS:
                return handleSuccess(judgeContext);
            default:
                // 处理其他情况，或者抛出异常
                throw new IllegalArgumentException("未知的状态：" + status);
        }
    }

    /**
     * 1、处理编译错误或者系统错误
     * @param judgeContext
     * @return
     */
    private JudgeInfo handleCompileOrSystemError(JudgeContext judgeContext) {
        String errorMessage = judgeContext.getMessage();
        if (errorMessage.contains("编译错误")) {
            judgeInfoMessageEnum = JudgeInfoMessageEnum.COMPILE_ERROR;
        } else {
            judgeInfoMessageEnum = JudgeInfoMessageEnum.SYSTEM_ERROR;
        }
        return createJudgeInfo(judgeInfoMessageEnum, 0L, 0L, 0);

    }

    /**
     * 2、处理运行错误
     * @return
     */
    private JudgeInfo handleRuntimeError(JudgeContext judgeContext) {
        // todo 运行错误如何进一步详细呢？从judgeContext中获取什么信息？
        judgeInfoMessageEnum = JudgeInfoMessageEnum.RUNNING_ERROR;
        return createJudgeInfo(judgeInfoMessageEnum, 0L, 0L, 0);
        
    }

    /**
     * 3、处理成功（时间空间以及人工测评）
     * @param judgeContext
     * @return
     */
    private JudgeInfo handleSuccess(JudgeContext judgeContext) {
        JudgeInfo judgeInfo = judgeContext.getJudgeInfo();
        Long time = Optional.ofNullable(judgeInfo.getTime()).orElse(0L);
        Long memory = Optional.ofNullable(judgeInfo.getMemory()).orElse(0L);
        List<String> inputList = judgeContext.getInputList();
        List<String> outputList = judgeContext.getOutputList();
        Question question = judgeContext.getQuestion();
        Integer isManual = judgeContext.getIsManual();
        List<JudgeCase> judgeCases = judgeContext.getJudgeCases();
        // 初始化
        judgeInfoMessageEnum = JudgeInfoMessageEnum.ACCEPTED;
        //判断题目限制
        String judgeConfigStr = question.getJudgeConfig();
        JudgeConfig judgeConfig = JSONUtil.toBean(judgeConfigStr, JudgeConfig.class);
        //先判断时间和空间限制
        Long needTimeLimit = judgeConfig.getTimeLimit();
        Long needMemoryLimit = judgeConfig.getMemoryLimit();
        //Long stackLimit = judgeConfig.getStackLimit();
        // 空间限制判断
        if (checkMemoryLimit(memory, needMemoryLimit)) {
            judgeInfoMessageEnum = JudgeInfoMessageEnum.MEMORY_LIMIT_EXCEEDED;
            return createJudgeInfo(judgeInfoMessageEnum, time, memory, 0);
        }
        // 时间限制判断
        if (checkTimeLimit(time, needTimeLimit)) {
            judgeInfoMessageEnum = JudgeInfoMessageEnum.TIME_LIMIT_EXCEEDED;
            return createJudgeInfo(judgeInfoMessageEnum, time, memory, 0);
        }
        // 如果时间和空间都没有问题。判断是否是人工测评，是就返回输出信息。不是就返回成功
        if(isManual == 1) {
            return handleManualEvaluation(judgeContext);
        }
        
        //判断数量是否相等，输入用例和返回的输出用例数量
        if (outputList.size() != inputList.size()) {
            judgeInfoMessageEnum = JudgeInfoMessageEnum.WRONG_ANSWER;
            return createJudgeInfo(judgeInfoMessageEnum, time, memory, 0);

        }
        //判断每一个输出用例是否相同
        for (int i = 0; i < judgeCases.size(); i++) {
            JudgeCase judgeCase = judgeCases.get(i);
            if (!judgeCase.getOutput().equals(outputList.get(i))) {
                judgeInfoMessageEnum = JudgeInfoMessageEnum.WRONG_ANSWER;
                return createJudgeInfo(judgeInfoMessageEnum, time, memory, 0);

            }
        }
        return createJudgeInfo(judgeInfoMessageEnum, time, memory, 1);
    }

    /**
     * 3.1、时间限制判断（Java可能需要久一些）
     * @param time
     * @param needTimeLimit
     * @return
     */
    public boolean checkTimeLimit(Long time, Long needTimeLimit){
        return time > needTimeLimit;
    }

    /**
     *3.2、空间限制判断
     * @param memory
     * @param needMemoryLimit
     * @return
     */
    public  boolean checkMemoryLimit(Long memory, Long needMemoryLimit){
        return memory > needMemoryLimit;
    }

    /**
     * 人工测评返回数据
     * @param judgeContext
     * @return
     */
    private JudgeInfo handleManualEvaluation(JudgeContext judgeContext) {
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setTime(judgeContext.getJudgeInfo().getTime());
        judgeInfo.setMemory(judgeContext.getJudgeInfo().getMemory());
        judgeInfo.setMessage(judgeContext.getOutputList().get(0));
        judgeInfo.setStatus(1);
        return judgeInfo;
    }

    /**
     * 题目提交返回数据
     * @param judgeInfoMessageEnum
     * @return
     */
    private JudgeInfo createJudgeInfo(JudgeInfoMessageEnum judgeInfoMessageEnum, Long time, Long memory, Integer status) {
        JudgeInfo judgeInfoResponse = new JudgeInfo();
        judgeInfoResponse.setTime(time);
        judgeInfoResponse.setMemory(memory);
        judgeInfoResponse.setMessage(judgeInfoMessageEnum.getText());
        judgeInfoResponse.setStatus(status);
        return judgeInfoResponse;
    }

}
